package com.atguigu.tingshu.order.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import com.atguigu.tingshu.account.AccountFeignClient;
import com.atguigu.tingshu.album.AlbumFeignClient;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.model.order.OrderDerate;
import com.atguigu.tingshu.model.order.OrderDetail;
import com.atguigu.tingshu.model.order.OrderInfo;
import com.atguigu.tingshu.order.helper.SignHelper;
import com.atguigu.tingshu.order.mapper.OrderDerateMapper;
import com.atguigu.tingshu.order.mapper.OrderInfoMapper;
import com.atguigu.tingshu.order.pattern.OrderConfirmPolicy;
import com.atguigu.tingshu.order.pattern.factory.OrderConfirmFactory;
import com.atguigu.tingshu.order.service.OrderDerateService;
import com.atguigu.tingshu.order.service.OrderDetailService;
import com.atguigu.tingshu.order.service.OrderInfoService;
import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.vo.account.AccountDeductVo;
import com.atguigu.tingshu.vo.order.OrderDerateVo;
import com.atguigu.tingshu.vo.order.OrderDetailVo;
import com.atguigu.tingshu.vo.order.OrderInfoVo;
import com.atguigu.tingshu.vo.order.TradeVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
//自动更新
@RefreshScope
@SuppressWarnings({"all"})
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {

    @Autowired
    private OrderInfoMapper orderInfoMapper;
    @Autowired
    private UserFeignClient userFeignClient;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private AlbumFeignClient albumFeignClient;
    @Autowired
    private OrderConfirmFactory orderConfirmFactory;
    @Autowired
    private OrderDetailService orderDetailService;
    @Autowired
    private OrderDerateService orderDerateService;
    @Autowired
    private AccountFeignClient accountFeignClient;
    @Autowired
    private RabbitService rabbitService;
    @Value("${order.cancel}")
    private int orderCancelDelayTime;

    /**
     * 订单确认(数据汇总)
     *
     * @param tradeVo 订单信息: ①付款项目类型:VIP/专辑/声音订单 ;② 付款项目ID ;③ 声音数量 required = false
     * @return orderInfoVo 订单详情
     */
    @Override
    public OrderInfoVo trade(TradeVo tradeVo, Long userId) {
        //获取付款项目类型
        String itemType = tradeVo.getItemType();
        OrderConfirmPolicy confirmPolicy = orderConfirmFactory.getOrderConfirmPolicy(itemType);
        OrderInfoVo orderInfoVo = confirmPolicy.confirm(tradeVo, userId);
        return orderInfoVo;
    }

    @GlobalTransactional(rollbackFor = Exception.class)
    @Override
    public Map<String, String> submitOrder(OrderInfoVo orderInfoVo, Long userId) {
        //1.业务校验：验证流水号，防止订单重复提交 采用Lua脚本保证判断删除原子性
        String tradeKey = RedisConstant.ORDER_TRADE_NO_PREFIX + userId;
        //1.1 定义lua脚本
        String luaScript = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
                "then\n" +
                "    return redis.call(\"del\",KEYS[1])\n" +
                "else\n" +
                "    return 0\n" +
                "end";
        //1.2 创建脚本对象
        DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(luaScript);
        redisScript.setResultType(Boolean.class);
        //1.3 执行Lua脚本
        Boolean flag = (Boolean) redisTemplate.execute(redisScript, Arrays.asList(tradeKey), orderInfoVo.getTradeNo());
        if (!flag) {
            throw new GuiguException(500, "验证流水号失败");
        }

        //2.业务校验：`验证签名`，防止订单数据被篡改
        //2.1 将订单VO转为Map对象 由于当时加签将payWay排除掉，并未加入到签名中
        Map<String, Object> map = BeanUtil.beanToMap(orderInfoVo);
        map.remove("payWay");
        //2.2 调用验证签名工具方法
        SignHelper.checkSign(map);

        //3.核心业务：保存订单以及订单明细，优惠列表等  -- 此时订单：未支付
        OrderInfo orderInfo = this.saveOrderInfo(orderInfoVo, userId);

        //支付方式：1101-微信 1102-支付宝 1103-账户余额
        String payWay = orderInfoVo.getPayWay();
        //4.判断支付方式：余额扣减核心业务
        if (SystemConstant.ORDER_PAY_ACCOUNT.equals(payWay)) {
            //4.1 远程调用账户服务扣减账户余额
            //4.1.1 封装扣减账户VO对象
            AccountDeductVo accountDeductVo = new AccountDeductVo();
            accountDeductVo.setOrderNo(orderInfo.getOrderNo());
            accountDeductVo.setUserId(orderInfo.getUserId());
            accountDeductVo.setAmount(orderInfo.getOrderAmount());
            accountDeductVo.setContent(orderInfo.getOrderTitle());
            //4.1.2 远程调用扣减账户余额
            Result result = accountFeignClient.checkAndDeduct(accountDeductVo);
            //4.1.3 判断远程调用结果业务状态码是否为200
            if (result.getCode().intValue() != 200) {
                throw new GuiguException(result.getCode(), result.getMessage());
            }
            //4.2 如果扣减余额成功，修改订单状态改为：已支付
            orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_PAID);
            orderInfoMapper.updateById(orderInfo);

            //4.3 远程调用用户服务虚拟物品发货
            //4.3.1 构建虚拟发货VO对象
            UserPaidRecordVo userPaidRecordVo = new UserPaidRecordVo();
            userPaidRecordVo.setOrderNo(orderInfo.getOrderNo());
            userPaidRecordVo.setUserId(orderInfo.getUserId());
            userPaidRecordVo.setItemType(orderInfo.getItemType());
            //根据订单编号查询订单明细获取购买项目ID列表
            List<OrderDetail> orderDetailList = orderDetailService.list(
                    new LambdaQueryWrapper<OrderDetail>()
                            .eq(OrderDetail::getOrderId, orderInfo.getId())
                            .select(OrderDetail::getItemId)
            );
            List<Long> itemIdList = orderDetailList.stream()
                    .map(OrderDetail::getItemId).collect(Collectors.toList());
            userPaidRecordVo.setItemIdList(itemIdList);

            //4.3.2 远程调用用户服务,发放用户权益
            result = userFeignClient.savePaidRecord(userPaidRecordVo);
            //4.3.3 判断远程调用结果业务状态码是否为200
            if (result.getCode().intValue() != 200) {
                throw new GuiguException(result.getCode(), result.getMessage());
            }
        }

        //5.判断支付方式：微信
        if (SystemConstant.ORDER_PAY_WAY_WEIXIN.equals(orderInfoVo.getPayWay())) {
            rabbitService.sendDelayMessage(MqConst.EXCHANGE_CANCEL_ORDER, MqConst.ROUTING_CANCEL_ORDER, orderInfo.getId(), orderCancelDelayTime);
        }

        //6.响应结果
        return Map.of("orderNo", orderInfo.getOrderNo());
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public OrderInfo saveOrderInfo(OrderInfoVo orderInfoVo, Long userId) {
        //1.保存订单
        //2.1 创建订单对象
        OrderInfo orderInfo = BeanUtil.copyProperties(orderInfoVo, OrderInfo.class);
        //2.2 设置用户ID
        orderInfo.setUserId(userId);
        //2.3 设置订单标题
        List<OrderDetailVo> orderDetailVoList = orderInfoVo.getOrderDetailVoList();
        if (CollUtil.isNotEmpty(orderDetailVoList)) {
            String itemName = orderDetailVoList.get(0).getItemName();
            orderInfo.setOrderTitle(itemName);
        }
        //2.4 订单状态：未支付
        orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_UNPAID);
        //2.5 设置订单编号 确保全局唯一且趋势递增 形式：当天日期+雪花算法
        String orderNo = DateUtil.today().replaceAll("-", "") + IdUtil.getSnowflakeNextId();
        orderInfo.setOrderNo(orderNo);
        //2.6 保存订单,获取订单ID
        orderInfoMapper.insert(orderInfo);
        Long orderId = orderInfo.getId();

        //2.保存订单明细列表
        if (CollUtil.isNotEmpty(orderDetailVoList)) {
            List<OrderDetail> orderDetailList = orderDetailVoList.stream().map(orderDetailVo -> {
                OrderDetail orderDetail = BeanUtil.copyProperties(orderDetailVo, OrderDetail.class);
                //关联订单ID
                orderDetail.setOrderId(orderId);
                return orderDetail;
            }).collect(Collectors.toList());
            orderDetailService.saveBatch(orderDetailList);
        }
        //3.保存订单减免列表
        List<OrderDerateVo> orderDerateVoList = orderInfoVo.getOrderDerateVoList();
        if (CollUtil.isNotEmpty(orderDerateVoList)) {
            List<OrderDerate> orderDerateList = orderDerateVoList.stream().map(orderDerateVo -> {
                OrderDerate orderDerate = BeanUtil.copyProperties(orderDerateVo, OrderDerate.class);
                orderDerate.setOrderId(orderId);
                return orderDerate;
            }).collect(Collectors.toList());
            orderDerateService.saveBatch(orderDerateList);
        }
        return orderInfo;
    }

    @Override
    public void cancelOrder(Long orderId) {
        OrderInfo orderInfo = orderInfoMapper.selectById(orderId);
        if (orderInfo.getOrderStatus().equals(SystemConstant.ORDER_STATUS_UNPAID)) {
            orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_CANCEL);
            orderInfoMapper.updateById(orderInfo);
        }
    }

    /**
     * 更新订单状态以及完成权益发放
     *
     * @param orderNo
     * @return
     */
    @Override
    public void orderPaySuccess(String orderNo) {
        //根据订单编号,查询订单信息,更新订单状态
        OrderInfo orderInfo = orderInfoMapper.selectOne(new LambdaQueryWrapper<OrderInfo>()
                .eq(OrderInfo::getOrderNo, orderNo));
        String orderStatus = orderInfo.getOrderStatus();
        orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_PAID);
        orderInfoMapper.updateById(orderInfo);

        //订单完成创建虚拟商品对象发货
        UserPaidRecordVo userPaidRecordVo = new UserPaidRecordVo();
        userPaidRecordVo.setUserId(orderInfo.getUserId());
        userPaidRecordVo.setOrderNo(orderInfo.getOrderNo());
        userPaidRecordVo.setItemType(orderInfo.getItemType());
        List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
        if (CollUtil.isNotEmpty(orderDetailList)) {
            List<Long> itemIdList = orderDetailList.stream().map(OrderDetail::getItemId).collect(Collectors.toList());
            userPaidRecordVo.setItemIdList(itemIdList);
            //调用用户服务,发放虚拟商品
            Result result = userFeignClient.savePaidRecord(userPaidRecordVo);
            if (result.getCode()!=200){
                throw new GuiguException(result.getCode(),result.getMessage());
            }
        }
    }

    @Override
    public Page<OrderInfo> findUserOrderPage(Page<OrderInfo> pageInfo, Long userId) {
        //1.方式一：获取当前用户订单列表,然后遍历订单获取每个订单明显列表
        //return orderInfoMapper.findUserOrderPage(pageInfo, userId);
        //2. 方式二：单独分页查询订单，在根据订单ID集合查询订单明细，封装订单明细列表
        Page<OrderInfo> infoPage = orderInfoMapper
                .selectPage(pageInfo,
                        new LambdaQueryWrapper<OrderInfo>().eq(OrderInfo::getUserId, userId)
                                .select(OrderInfo::getId, OrderInfo::getOrderNo, OrderInfo::getOrderStatus, OrderInfo::getPayWay, OrderInfo::getOriginalAmount, OrderInfo::getOrderAmount, OrderInfo::getDerateAmount)
                                .orderByDesc(OrderInfo::getId)
                );

        //3.获取订单ID集合
        List<Long> orderIdList = infoPage.getRecords().stream().map(OrderInfo::getId).collect(Collectors.toList());

        //4.查询所有订单明细列表
        List<OrderDetail> orderDetailList = orderDetailService.list(
                new LambdaQueryWrapper<OrderDetail>()
                        .in(OrderDetail::getOrderId, orderIdList)
                        .select(OrderDetail::getOrderId, OrderDetail::getItemId, OrderDetail::getItemName, OrderDetail::getItemPrice, OrderDetail::getItemUrl)
        );
        //5.将所有订单明细列表转为Map<Long, List<OrderDetail>> Key为订单ID Value：订单明细列表
        Map<Long, List<OrderDetail>> orderDetailListMap =
                orderDetailList.stream().collect(Collectors.groupingBy(OrderDetail::getOrderId));
        for (OrderInfo record : infoPage.getRecords()) {
            record.setOrderDetailList(orderDetailListMap.get(record.getId()));
        }
        return infoPage;
    }
}
